home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / window.c < prev   
C/C++ Source or Header  |  1996-06-09  |  34KB  |  1,570 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read a list of people who contributed.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. #include "vim.h"
  10. #include "globals.h"
  11. #include "proto.h"
  12. #include "option.h"
  13.  
  14. static void reset_VIsual __ARGS((void));
  15. static int win_comp_pos __ARGS((void));
  16. static void win_exchange __ARGS((long));
  17. static void win_rotate __ARGS((int, int));
  18. static void win_append __ARGS((WIN *, WIN *));
  19. static void win_remove __ARGS((WIN *));
  20. static void win_new_height __ARGS((WIN *, int));
  21.  
  22. static WIN        *prevwin = NULL;        /* previous window */
  23.  
  24. /*
  25.  * all CTRL-W window commands are handled here, called from normal().
  26.  */
  27.     void
  28. do_window(nchar, Prenum)
  29.     int        nchar;
  30.     long    Prenum;
  31. {
  32.     long    Prenum1;
  33.     WIN        *wp;
  34.     char_u    *ptr;
  35.     int        len;
  36.     int        type = -1;
  37.     WIN        *wp2;
  38.  
  39.     if (Prenum == 0)
  40.         Prenum1 = 1;
  41.     else
  42.         Prenum1 = Prenum;
  43.  
  44.     switch (nchar)
  45.     {
  46. /* split current window in two parts */
  47.     case 'S':
  48.     case Ctrl('S'):
  49.     case 's':    reset_VIsual();                    /* stop Visual mode */
  50.                 win_split((int)Prenum, TRUE);
  51.                 break;
  52.  
  53. /* split current window and edit alternate file */
  54.     case K_CCIRCM:
  55.     case '^':
  56.                 reset_VIsual();                    /* stop Visual mode */
  57.                 stuffReadbuff((char_u *)":split #");
  58.                 if (Prenum)
  59.                     stuffnumReadbuff(Prenum);    /* buffer number */
  60.                 stuffcharReadbuff('\n');
  61.                 break;
  62.  
  63. /* open new window */
  64.     case Ctrl('N'):
  65.     case 'n':    reset_VIsual();                    /* stop Visual mode */
  66.                 stuffcharReadbuff(':');
  67.                 if (Prenum)
  68.                     stuffnumReadbuff(Prenum);        /* window height */
  69.                 stuffReadbuff((char_u *)"new\n");    /* it is cmdline.c */
  70.                 break;
  71.  
  72. /* quit current window */
  73.     case Ctrl('Q'):
  74.     case 'q':    reset_VIsual();                    /* stop Visual mode */
  75.                 stuffReadbuff((char_u *)":quit\n");    /* it is cmdline.c */
  76.                 break;
  77.  
  78. /* close current window */
  79.     case Ctrl('C'):
  80.     case 'c':    reset_VIsual();                    /* stop Visual mode */
  81.                 stuffReadbuff((char_u *)":close\n");    /* it is cmdline.c */
  82.                 break;
  83.  
  84. /* close all but current window */
  85.     case Ctrl('O'):
  86.     case 'o':    reset_VIsual();                    /* stop Visual mode */
  87.                 stuffReadbuff((char_u *)":only\n");    /* it is cmdline.c */
  88.                 break;
  89.  
  90. /* cursor to next window */
  91.     case 'j':
  92.     case K_DOWN:
  93.     case Ctrl('J'):
  94.                 for (wp = curwin; wp->w_next != NULL && Prenum1-- > 0;
  95.                                                             wp = wp->w_next)
  96.                     ;
  97. new_win:
  98.                 /*
  99.                  * When jumping to another buffer, stop visual mode
  100.                  * Do this before changing windows so we can yank the
  101.                  * selection into the '"*' register.
  102.                  */
  103.                 if (wp->w_buffer != curbuf && VIsual_active)
  104.                 {
  105.                     end_visual_mode();
  106.                     for (wp2 = firstwin; wp2 != NULL; wp2 = wp2->w_next)
  107.                         if (wp2->w_buffer == curbuf &&
  108.                                             wp2->w_redr_type < NOT_VALID)
  109.                         {
  110.                             wp2->w_redr_type = NOT_VALID;
  111.                             redraw_later(NOT_VALID);
  112.                         }
  113.                 }
  114.                 win_enter(wp, TRUE);
  115.                 cursupdate();
  116.                 break;
  117.  
  118. /* cursor to next window with wrap around */
  119.     case Ctrl('W'):
  120.     case 'w':
  121. /* cursor to previous window with wrap around */
  122.     case 'W':
  123.                 if (lastwin == firstwin)        /* just one window */
  124.                     beep_flush();
  125.                 else
  126.                 {
  127.                     if (Prenum)                    /* go to specified window */
  128.                     {
  129.                         for (wp = firstwin; --Prenum > 0; )
  130.                         {
  131.                             if (wp->w_next == NULL)
  132.                                 break;
  133.                             else
  134.                                 wp = wp->w_next;
  135.                         }
  136.                     }
  137.                     else
  138.                     {
  139.                         if (nchar == 'W')            /* go to previous window */
  140.                         {
  141.                             wp = curwin->w_prev;
  142.                             if (wp == NULL)
  143.                                 wp = lastwin;        /* wrap around */
  144.                         }
  145.                         else                        /* go to next window */
  146.                         {
  147.                             wp = curwin->w_next;
  148.                             if (wp == NULL)
  149.                                 wp = firstwin;        /* wrap around */
  150.                         }
  151.                     }
  152.                     goto new_win;
  153.                 }
  154.                 break;
  155.  
  156. /* cursor to window above */
  157.     case 'k':
  158.     case K_UP:
  159.     case Ctrl('K'):
  160.                 for (wp = curwin; wp->w_prev != NULL && Prenum1-- > 0;
  161.                                                             wp = wp->w_prev)
  162.                     ;
  163.                 goto new_win;
  164.  
  165. /* cursor to top window */
  166.     case 't':
  167.     case Ctrl('T'):
  168.                 wp = firstwin;
  169.                 goto new_win;
  170.  
  171. /* cursor to bottom window */
  172.     case 'b':
  173.     case Ctrl('B'):
  174.                 wp = lastwin;
  175.                 goto new_win;
  176.  
  177. /* cursor to last accessed (previous) window */
  178.     case 'p':
  179.     case Ctrl('P'):
  180.                 if (prevwin == NULL)
  181.                     beep_flush();
  182.                 else
  183.                 {
  184.                     wp = prevwin;
  185.                     goto new_win;
  186.                 }
  187.                 break;
  188.  
  189. /* exchange current and next window */
  190.     case 'x':
  191.     case Ctrl('X'):
  192.                 win_exchange(Prenum);
  193.                 break;
  194.  
  195. /* rotate windows downwards */
  196.     case Ctrl('R'):
  197.     case 'r':    reset_VIsual();                    /* stop Visual mode */
  198.                 win_rotate(FALSE, (int)Prenum1);    /* downwards */
  199.                 break;
  200.  
  201. /* rotate windows upwards */
  202.     case 'R':    reset_VIsual();                    /* stop Visual mode */
  203.                 win_rotate(TRUE, (int)Prenum1);        /* upwards */
  204.                 break;
  205.  
  206. /* make all windows the same height */
  207.     case '=':    win_equal(NULL, TRUE);
  208.                 break;
  209.  
  210. /* increase current window height */
  211.     case '+':    win_setheight(curwin->w_height + (int)Prenum1);
  212.                 break;
  213.  
  214. /* decrease current window height */
  215.     case '-':    win_setheight(curwin->w_height - (int)Prenum1);
  216.                 break;
  217.  
  218. /* set current window height */
  219.     case Ctrl('_'):
  220.     case '_':    win_setheight(Prenum ? (int)Prenum : 9999);
  221.                 break;
  222.  
  223. /* jump to tag and split window if tag exists */
  224.     case ']':
  225.     case Ctrl(']'):
  226.                 reset_VIsual();                    /* stop Visual mode */
  227.                 postponed_split = TRUE;
  228.                 stuffcharReadbuff(Ctrl(']'));
  229.                 break;
  230.  
  231. /* edit file name under cursor in a new window */
  232.     case 'f':
  233.     case Ctrl('F'):
  234.                 reset_VIsual();                    /* stop Visual mode */
  235.                 ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP);
  236.                 if (ptr != NULL)
  237.                 {
  238.                     setpcmark();
  239.                     if (win_split(0, FALSE) == OK)
  240.                         (void)do_ecmd(0, ptr, NULL, NULL, p_hid, (linenr_t)0, FALSE);
  241.                     vim_free(ptr);
  242.                 }
  243.                 break;
  244.  
  245. /* Go to the first occurence of the identifier under cursor along path in a
  246.  * new window -- webb
  247.  */
  248.     case 'i':                        /* Go to any match */
  249.     case Ctrl('I'):
  250.                 type = FIND_ANY;
  251.                 /* FALLTHROUGH */
  252.     case 'd':                        /* Go to definition, using p_def */
  253.     case Ctrl('D'):
  254.                 if (type == -1)
  255.                     type = FIND_DEFINE;
  256.  
  257.                 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
  258.                     break;
  259.                 find_pattern_in_path(ptr, len, TRUE, TRUE, type,
  260.                        Prenum1, ACTION_SPLIT, (linenr_t)1, (linenr_t)MAXLNUM);
  261.                 curwin->w_set_curswant = TRUE;
  262.                 break;
  263.  
  264.     default:    beep_flush();
  265.                 break;
  266.     }
  267. }
  268.  
  269.     static void
  270. reset_VIsual()
  271. {
  272.     if (VIsual_active)
  273.     {
  274.         end_visual_mode();
  275.         update_curbuf(NOT_VALID);        /* delete the inversion */
  276.     }
  277. }
  278.  
  279. /*
  280.  * split the current window, implements CTRL-W s and :split
  281.  *
  282.  * new_height is the height for the new window, 0 to make half of current
  283.  * height redraw is TRUE when redraw now
  284.  *
  285.  * return FAIL for failure, OK otherwise
  286.  */
  287.     int
  288. win_split(new_height, redraw)
  289.     int        new_height;
  290.     int        redraw;
  291. {
  292.     WIN            *wp;
  293.     linenr_t    lnum;
  294.     int            h;
  295.     int            i;
  296.     int            need_status;
  297.     int            do_equal = (p_ea && new_height == 0);
  298.     int            needed;
  299.     int            available;
  300.     int            curwin_height;
  301.     
  302.         /* add a status line when p_ls == 1 and splitting the first window */
  303.     if (lastwin == firstwin && p_ls == 1 && curwin->w_status_height == 0)
  304.         need_status = STATUS_HEIGHT;
  305.     else
  306.         need_status = 0;
  307.  
  308. /*
  309.  * check if we are able to split the current window and compute its height
  310.  */
  311.     available = curwin->w_height;
  312.      needed = 2 * MIN_ROWS + STATUS_HEIGHT + need_status;
  313.     if (p_ea)
  314.     {
  315.         for (wp = firstwin; wp != NULL; wp = wp->w_next)
  316.             if (wp != curwin)
  317.             {
  318.                 available += wp->w_height;
  319.                 needed += MIN_ROWS;
  320.             }
  321.     }
  322.      if (available < needed)
  323.     {
  324.         EMSG(e_noroom);
  325.         return FAIL;
  326.     }
  327.     curwin_height = curwin->w_height;
  328.     if (need_status)
  329.     {
  330.         curwin->w_status_height = STATUS_HEIGHT;
  331.         curwin_height -= STATUS_HEIGHT;
  332.     }
  333.     if (new_height == 0)
  334.         new_height = curwin_height / 2;
  335.  
  336.     if (new_height > curwin_height - MIN_ROWS - STATUS_HEIGHT)
  337.         new_height = curwin_height - MIN_ROWS - STATUS_HEIGHT;
  338.  
  339.     if (new_height < MIN_ROWS)
  340.         new_height = MIN_ROWS;
  341.  
  342.         /* if it doesn't fit in the current window, need win_equal() */
  343.     if (curwin_height - new_height - STATUS_HEIGHT < MIN_ROWS)
  344.         do_equal = TRUE;
  345. /*
  346.  * allocate new window structure and link it in the window list
  347.  */
  348.     if (p_sb)        /* new window below current one */
  349.         wp = win_alloc(curwin);
  350.     else
  351.         wp = win_alloc(curwin->w_prev);
  352.     if (wp == NULL)
  353.         return FAIL;
  354. /*
  355.  * compute the new screen positions
  356.  */
  357.     win_new_height(wp, new_height);
  358.     win_new_height(curwin, curwin_height - (new_height + STATUS_HEIGHT));
  359.     if (p_sb)        /* new window below current one */
  360.     {
  361.         wp->w_winpos = curwin->w_winpos + curwin->w_height + STATUS_HEIGHT;
  362.         wp->w_status_height = curwin->w_status_height;
  363.         curwin->w_status_height = STATUS_HEIGHT;
  364.     }
  365.     else            /* new window above current one */
  366.     {
  367.         wp->w_winpos = curwin->w_winpos;
  368.         wp->w_status_height = STATUS_HEIGHT;
  369.         curwin->w_winpos = wp->w_winpos + wp->w_height + STATUS_HEIGHT;
  370.     }
  371. /*
  372.  * make the contents of the new window the same as the current one
  373.  */
  374.     wp->w_buffer = curbuf;
  375.     curbuf->b_nwindows++;
  376.     wp->w_cursor = curwin->w_cursor;
  377.     wp->w_row = curwin->w_row;
  378.     wp->w_col = curwin->w_col;
  379.     wp->w_virtcol = curwin->w_virtcol;
  380.     wp->w_curswant = curwin->w_curswant;
  381.     wp->w_set_curswant = curwin->w_set_curswant;
  382.     wp->w_empty_rows = curwin->w_empty_rows;
  383.     wp->w_leftcol = curwin->w_leftcol;
  384.     wp->w_pcmark = curwin->w_pcmark;
  385.     wp->w_prev_pcmark = curwin->w_prev_pcmark;
  386.     wp->w_alt_fnum = curwin->w_alt_fnum;
  387.  
  388.     wp->w_arg_idx = curwin->w_arg_idx;
  389.     /*
  390.      * copy tagstack and options from existing window
  391.      */
  392.     for (i = 0; i < curwin->w_tagstacklen; i++)
  393.     {
  394.         wp->w_tagstack[i].fmark = curwin->w_tagstack[i].fmark;
  395.         wp->w_tagstack[i].tagname = strsave(curwin->w_tagstack[i].tagname);
  396.     }
  397.     wp->w_tagstackidx = curwin->w_tagstackidx;
  398.     wp->w_tagstacklen = curwin->w_tagstacklen;
  399.     win_copy_options(curwin, wp);
  400. /*
  401.  * Both windows need redrawing
  402.  */
  403.      wp->w_redr_type = NOT_VALID;
  404.     wp->w_redr_status = TRUE;
  405.      curwin->w_redr_type = NOT_VALID;
  406.     curwin->w_redr_status = TRUE;
  407. /*
  408.  * Cursor is put in middle of window in both windows
  409.  */
  410.     if (wp->w_height < curwin->w_height)    /* use smallest of two heights */
  411.         h = wp->w_height;
  412.     else
  413.         h = curwin->w_height;
  414.     h >>= 1;
  415.     for (lnum = wp->w_cursor.lnum; lnum > 1; --lnum)
  416.     {
  417.         h -= plines(lnum);
  418.         if (h <= 0)
  419.             break;
  420.     }
  421.     wp->w_topline = lnum;
  422.     curwin->w_topline = lnum;
  423.     if (need_status)
  424.     {
  425.         msg_pos((int)Rows - 1, sc_col);
  426.         msg_clr_eos();        /* Old command/ruler may still be there -- webb */
  427.         comp_col();
  428.         msg_pos((int)Rows - 1, 0);    /* put position back at start of line */
  429.     }
  430. /*
  431.  * make the new window the current window and redraw
  432.  */
  433.     if (do_equal)
  434.         win_equal(wp, FALSE);
  435.      win_enter(wp, FALSE);
  436.  
  437.     if (redraw)
  438.         updateScreen(NOT_VALID);
  439.  
  440.     return OK;
  441. }
  442.  
  443. /*
  444.  * Return the number of windows.
  445.  */
  446.     int
  447. win_count()
  448. {
  449.     WIN     *wp;
  450.     int        count = 0;
  451.  
  452.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  453.         ++count;
  454.     return count;
  455. }
  456.  
  457. /*
  458.  * Make 'count' windows on the screen.
  459.  * Return actual number of windows on the screen.
  460.  * Must be called when there is just one window, filling the whole screen
  461.  * (excluding the command line).
  462.  */
  463.     int
  464. make_windows(count)
  465.     int        count;
  466. {
  467.     int        maxcount;
  468.     int        todo;
  469.     int        p_sb_save;
  470.  
  471. /*
  472.  * Each window needs at least MIN_ROWS lines and a status line.
  473.  * Add 4 lines for one window, otherwise we may end up with all one-line
  474.  * windows. Use value of 'winheight' if it is set
  475.  */
  476.     maxcount = (curwin->w_height + curwin->w_status_height -
  477.                         (p_wh ? (p_wh - 1) : 4)) / (MIN_ROWS + STATUS_HEIGHT);
  478.     if (maxcount < 2)
  479.         maxcount = 2;
  480.     if (count > maxcount)
  481.         count = maxcount;
  482.  
  483.     /*
  484.      * add status line now, otherwise first window will be too big
  485.      */
  486.     if ((p_ls == 2 || (count > 1 && p_ls == 1)) && curwin->w_status_height == 0)
  487.     {
  488.         curwin->w_status_height = STATUS_HEIGHT;
  489.         win_new_height(curwin, curwin->w_height - STATUS_HEIGHT);
  490.     }
  491.  
  492. /*
  493.  * set 'splitbelow' off for a moment, don't want that now
  494.  */
  495.     p_sb_save = p_sb;
  496.     p_sb = FALSE;
  497.         /* todo is number of windows left to create */
  498.     for (todo = count - 1; todo > 0; --todo)
  499.         if (win_split(curwin->w_height - (curwin->w_height - todo
  500.                 * STATUS_HEIGHT) / (todo + 1) - STATUS_HEIGHT, FALSE) == FAIL)
  501.             break;
  502.     p_sb = p_sb_save;
  503.  
  504.         /* return actual number of windows */
  505.     return (count - todo);
  506. }
  507.  
  508. /*
  509.  * Exchange current and next window
  510.  */
  511.     static void
  512. win_exchange(Prenum)
  513.     long        Prenum;
  514. {
  515.     WIN        *wp;
  516.     WIN        *wp2;
  517.     int        temp;
  518.  
  519.     if (lastwin == firstwin)        /* just one window */
  520.     {
  521.         beep_flush();
  522.         return;
  523.     }
  524.  
  525. /*
  526.  * find window to exchange with
  527.  */
  528.     if (Prenum)
  529.     {
  530.         wp = firstwin;
  531.         while (wp != NULL && --Prenum > 0)
  532.             wp = wp->w_next;
  533.     }
  534.     else if (curwin->w_next != NULL)    /* Swap with next */
  535.         wp = curwin->w_next;
  536.     else    /* Swap last window with previous */
  537.         wp = curwin->w_prev;
  538.  
  539.     if (wp == curwin || wp == NULL)
  540.         return;
  541.  
  542. /*
  543.  * 1. remove curwin from the list. Remember after which window it was in wp2
  544.  * 2. insert curwin before wp in the list
  545.  * if wp != wp2
  546.  *    3. remove wp from the list
  547.  *    4. insert wp after wp2
  548.  * 5. exchange the status line height
  549.  */
  550.     wp2 = curwin->w_prev;
  551.     win_remove(curwin);
  552.     win_append(wp->w_prev, curwin);
  553.     if (wp != wp2)
  554.     {
  555.         win_remove(wp);
  556.         win_append(wp2, wp);
  557.     }
  558.     temp = curwin->w_status_height;
  559.     curwin->w_status_height = wp->w_status_height;
  560.     wp->w_status_height = temp;
  561.  
  562.     win_comp_pos();                /* recompute window positions */
  563.  
  564.     win_enter(wp, TRUE);
  565.     cursupdate();
  566.     updateScreen(CLEAR);
  567.  
  568. #ifdef USE_GUI
  569.     if (gui.in_use)
  570.     {
  571.         if (gui.which_scrollbars[SB_LEFT])
  572.             gui_mch_reorder_scrollbars(SB_LEFT);
  573.         if (gui.which_scrollbars[SB_RIGHT])
  574.             gui_mch_reorder_scrollbars(SB_RIGHT);
  575.     }
  576. #endif
  577. }
  578.  
  579. /*
  580.  * rotate windows: if upwards TRUE the second window becomes the first one
  581.  *                   if upwards FALSE the first window becomes the second one
  582.  */
  583.     static void
  584. win_rotate(upwards, count)
  585.     int        upwards;
  586.     int        count;
  587. {
  588.     WIN             *wp;
  589.     int             height;
  590.  
  591.     if (firstwin == lastwin)            /* nothing to do */
  592.     {
  593.         beep_flush();
  594.         return;
  595.     }
  596.     while (count--)
  597.     {
  598.         if (upwards)            /* first window becomes last window */
  599.         {
  600.             wp = firstwin;
  601.             win_remove(wp);
  602.             win_append(lastwin, wp);
  603.             wp = lastwin->w_prev;            /* previously last window */
  604.         }
  605.         else                    /* last window becomes first window */
  606.         {
  607.             wp = lastwin;
  608.             win_remove(lastwin);
  609.             win_append(NULL, wp);
  610.             wp = firstwin;                    /* previously last window */
  611.         }
  612.             /* exchange status height of old and new last window */
  613.         height = lastwin->w_status_height;
  614.         lastwin->w_status_height = wp->w_status_height;
  615.         wp->w_status_height = height;
  616.  
  617.             /* recompute w_winpos for all windows */
  618.         (void) win_comp_pos();
  619.     }
  620.  
  621.     cursupdate();
  622.     updateScreen(CLEAR);
  623.  
  624. #ifdef USE_GUI
  625.     if (gui.in_use)
  626.     {
  627.         if (gui.which_scrollbars[SB_LEFT])
  628.             gui_mch_reorder_scrollbars(SB_LEFT);
  629.         if (gui.which_scrollbars[SB_RIGHT])
  630.             gui_mch_reorder_scrollbars(SB_RIGHT);
  631.     }
  632. #endif
  633. }
  634.  
  635. /*
  636.  * Make all windows the same height.
  637.  * 'next_curwin' will soon be the current window, make sure it has enough
  638.  * rows.
  639.  */
  640.     void
  641. win_equal(next_curwin, redraw)
  642.     WIN        *next_curwin;            /* pointer to current window to be */
  643.     int        redraw;
  644. {
  645.     int        total;
  646.     int        less;
  647.     int        wincount;
  648.     int        winpos;
  649.     int        temp;
  650.     WIN        *wp;
  651.     int        new_height;
  652.  
  653. /*
  654.  * count the number of lines available
  655.  */
  656.     total = 0;
  657.     wincount = 0;
  658.     for (wp = firstwin; wp; wp = wp->w_next)
  659.     {
  660.         total += wp->w_height - MIN_ROWS;
  661.         wincount++;
  662.     }
  663.  
  664. /*
  665.  * If next_curwin given and 'winheight' set, make next_curwin p_wh lines.
  666.  */
  667.     less = 0;
  668.     if (next_curwin != NULL)
  669.     {
  670.         if (p_wh)
  671.         {
  672.             if (p_wh - MIN_ROWS > total)    /* all lines go to current window */
  673.                 less = total;
  674.             else
  675.             {
  676.                 less = p_wh - MIN_ROWS - total / wincount;
  677.                 if (less < 0)
  678.                     less = 0;
  679.             }
  680.         }
  681.     }
  682.  
  683. /*
  684.  * spread the available lines over the windows
  685.  */
  686.     winpos = 0;
  687.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  688.     {
  689.         if (wp == next_curwin && less)
  690.         {
  691.             less = 0;
  692.             temp = p_wh - MIN_ROWS;
  693.             if (temp > total)
  694.                 temp = total;
  695.         }
  696.         else
  697.             temp = (total - less + (wincount >> 1)) / wincount;
  698.         new_height = MIN_ROWS + temp;
  699.         if (wp->w_winpos != winpos || wp->w_height != new_height)
  700.         {
  701.             wp->w_redr_type = NOT_VALID;
  702.             wp->w_redr_status = TRUE;
  703.         }
  704.         wp->w_winpos = winpos;
  705.         win_new_height(wp, new_height);
  706.         total -= temp;
  707.         --wincount;
  708.         winpos += wp->w_height + wp->w_status_height;
  709.     }
  710.     if (redraw)
  711.     {
  712.         cursupdate();
  713.         updateScreen(CLEAR);
  714.     }
  715. }
  716.  
  717. /*
  718.  * close all windows for buffer 'buf'
  719.  */
  720.     void
  721. close_windows(buf)
  722.     BUF        *buf;
  723. {
  724.     WIN     *win;
  725.  
  726.     ++RedrawingDisabled;
  727.     for (win = firstwin; win != NULL && lastwin != firstwin; )
  728.     {
  729.         if (win->w_buffer == buf)
  730.         {
  731.             close_window(win, FALSE);
  732.             win = firstwin;            /* go back to the start */
  733.         }
  734.         else
  735.             win = win->w_next;
  736.     }
  737.     --RedrawingDisabled;
  738. }
  739.  
  740. /*
  741.  * close window "win"
  742.  * If "free_buf" is TRUE related buffer may be freed.
  743.  *
  744.  * called by :quit, :close, :xit, :wq and findtag()
  745.  */
  746.     void
  747. close_window(win, free_buf)
  748.     WIN        *win;
  749.     int        free_buf;
  750. {
  751.     WIN     *wp;
  752.  
  753.     if (lastwin == firstwin)
  754.     {
  755.         EMSG("Cannot close last window");
  756.         return;
  757.     }
  758.  
  759. /*
  760.  * Remove the window.
  761.  * if 'splitbelow' the free space goes to the window above it.
  762.  * if 'nosplitbelow' the free space goes to the window below it.
  763.  * This makes opening a window and closing it immediately keep the same window
  764.  * layout.
  765.  */
  766.                                     /* freed space goes to next window */
  767.     if ((!p_sb && win->w_next != NULL) || win->w_prev == NULL)
  768.     {
  769.         wp = win->w_next;
  770.         wp->w_winpos = win->w_winpos;
  771.     }
  772.     else                            /* freed space goes to previous window */
  773.         wp = win->w_prev;
  774.     win_new_height(wp, wp->w_height + win->w_height + win->w_status_height);
  775.  
  776. #ifdef AUTOCMD
  777.     if (win == curwin)
  778.     {
  779.         if (wp->w_buffer != curbuf)
  780.             apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
  781.         apply_autocmds(EVENT_WINLEAVE, NULL, NULL);
  782.     }
  783. #endif
  784.  
  785. /*
  786.  * Close the link to the buffer.
  787.  */
  788.     close_buffer(win, win->w_buffer, free_buf, FALSE);
  789.  
  790.     win_free(win);
  791.     if (win == curwin)
  792.         curwin = NULL;
  793.     if (p_ea)
  794.         win_equal(wp, FALSE);
  795.     if (curwin == NULL)
  796.         win_enter(wp, FALSE);
  797.     /*
  798.      * if last window has status line now and we don't want one,
  799.      * remove the status line
  800.      */
  801.     if (lastwin->w_status_height &&
  802.                         (p_ls == 0 || (p_ls == 1 && firstwin == lastwin)))
  803.     {
  804.         win_new_height(lastwin, lastwin->w_height + lastwin->w_status_height);
  805.         lastwin->w_status_height = 0;
  806.         comp_col();
  807.     }
  808.  
  809.     updateScreen(NOT_VALID);
  810.     if (RedrawingDisabled)
  811.         comp_Botline(wp);            /* need to do this before cursupdate() */
  812. }
  813.  
  814. /*
  815.  * close all windows except current one
  816.  * buffers in the windows become hidden
  817.  *
  818.  * called by :only and do_arg_all();
  819.  */
  820.     void
  821. close_others(message)
  822.     int        message;
  823. {
  824.     WIN     *wp;
  825.     WIN     *nextwp;
  826.  
  827.     if (lastwin == firstwin)
  828.     {
  829.         if (message
  830. #ifdef AUTOCMD
  831.                     && !autocmd_busy
  832. #endif
  833.                                     )
  834.             MSG("Already only one window");
  835.         return;
  836.     }
  837.  
  838.     for (wp = firstwin; wp != NULL; wp = nextwp)
  839.     {
  840.         nextwp = wp->w_next;
  841.         if (wp == curwin)                /* don't close current window */
  842.             continue;
  843.         /*
  844.          * Close the link to the buffer.
  845.          */
  846.         close_buffer(wp, wp->w_buffer, FALSE, FALSE);
  847.  
  848.         /*
  849.          * Remove the window. All lines go to current window.
  850.          */
  851.         win_new_height(curwin,
  852.                        curwin->w_height + wp->w_height + wp->w_status_height);
  853.         win_free(wp);
  854.     }
  855.  
  856.     /*
  857.      * If current window has status line and we don't want one,
  858.      * remove the status line.
  859.      */
  860.     if (curwin->w_status_height && p_ls != 2)
  861.     {
  862.         win_new_height(curwin, curwin->w_height + curwin->w_status_height);
  863.         curwin->w_status_height = 0;
  864.     }
  865.     curwin->w_winpos = 0;        /* put current window at top of the screen */
  866.     if (message)
  867.         updateScreen(NOT_VALID);
  868. }
  869.  
  870. /*
  871.  * init the cursor in the window
  872.  *
  873.  * called when a new file is being edited
  874.  */
  875.     void
  876. win_init(wp)
  877.     WIN        *wp;
  878. {
  879.     wp->w_redr_type = NOT_VALID;
  880.     wp->w_cursor.lnum = 1;
  881.     wp->w_curswant = wp->w_cursor.col = 0;
  882.     wp->w_pcmark.lnum = 1;        /* pcmark not cleared but set to line 1 */
  883.     wp->w_pcmark.col = 0;
  884.     wp->w_prev_pcmark.lnum = 0;
  885.     wp->w_prev_pcmark.col = 0;
  886.     wp->w_topline = 1;
  887.     wp->w_botline = 2;
  888. }
  889.  
  890. /*
  891.  * make window wp the current window
  892.  */
  893.     void
  894. win_enter(wp, undo_sync)
  895.     WIN        *wp;
  896.     int        undo_sync;
  897. {
  898. #ifdef AUTOCMD
  899.     int            other_buffer = FALSE;
  900. #endif
  901.  
  902.     if (wp == curwin)            /* nothing to do */
  903.         return;
  904.  
  905. #ifdef AUTOCMD
  906.     if (curwin != NULL)
  907.     {
  908.         if (wp->w_buffer != curbuf)
  909.         {
  910.             apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
  911.             other_buffer = TRUE;
  912.         }
  913.         apply_autocmds(EVENT_WINLEAVE, NULL, NULL);
  914.     }
  915. #endif
  916.  
  917.         /* sync undo before leaving the current buffer */
  918.     if (undo_sync && curbuf != wp->w_buffer)
  919.         u_sync();
  920.         /* may have to copy the buffer options when 'cpo' contains 'S' */
  921.     if (wp->w_buffer != curbuf)
  922.         buf_copy_options(curbuf, wp->w_buffer, TRUE);
  923.     if (curwin != NULL)
  924.         prevwin = curwin;        /* remember for CTRL-W p */
  925.     curwin = wp;
  926.     curbuf = wp->w_buffer;
  927.  
  928. #ifdef AUTOCMD
  929.     apply_autocmds(EVENT_WINENTER, NULL, NULL);
  930.     if (other_buffer)
  931.         apply_autocmds(EVENT_BUFENTER, NULL, NULL);
  932. #endif
  933.  
  934.     maketitle();
  935.             /* set window height to desired minimal value */
  936.     if (p_wh && curwin->w_height < p_wh)
  937.         win_setheight((int)p_wh);
  938. #ifdef USE_MOUSE
  939.     setmouse();                    /* in case jumped to/from help buffer */
  940. #endif
  941. }
  942.  
  943. /*
  944.  * allocate a window structure and link it in the window list
  945.  */
  946.     WIN *
  947. win_alloc(after)
  948.     WIN        *after;
  949. {
  950.     WIN        *newwin;
  951.  
  952. /*
  953.  * allocate window structure and linesizes arrays
  954.  */
  955.     newwin = (WIN *)alloc((unsigned)sizeof(WIN));
  956.     if (newwin)
  957.     {
  958. /*
  959.  * most stucture members have to be zero
  960.  */
  961.          (void)vim_memset(newwin, 0, sizeof(WIN));
  962. /*
  963.  * link the window in the window list
  964.  */
  965.         win_append(after, newwin);
  966.  
  967.         win_alloc_lsize(newwin);
  968.  
  969.         /* position the display and the cursor at the top of the file. */
  970.         newwin->w_topline = 1;
  971.         newwin->w_botline = 2;
  972.         newwin->w_cursor.lnum = 1;
  973.  
  974. #ifdef USE_GUI
  975.         /* Let the GUI know this is a new scrollbar */
  976.         newwin->w_scrollbar.height = 0;
  977. #endif /* USE_GUI */
  978.     }
  979.     return newwin;
  980. }
  981.  
  982. /*
  983.  * remove window 'wp' from the window list and free the structure
  984.  */
  985.     void
  986. win_free(wp)
  987.     WIN        *wp;
  988. {
  989.     int        i;
  990.  
  991.     if (prevwin == wp)
  992.         prevwin = NULL;
  993.     win_free_lsize(wp);
  994.  
  995.     for (i = 0; i < wp->w_tagstacklen; ++i)
  996.         free(wp->w_tagstack[i].tagname);
  997.  
  998. #ifdef USE_GUI
  999.     if (gui.in_use)
  1000.         gui_mch_destroy_scrollbar(wp);
  1001. #endif /* USE_GUI */
  1002.  
  1003.     win_remove(wp);
  1004.     vim_free(wp);
  1005. }
  1006.  
  1007.     static void
  1008. win_append(after, wp)
  1009.     WIN        *after, *wp;
  1010. {
  1011.     WIN     *before;
  1012.  
  1013.     if (after == NULL)        /* after NULL is in front of the first */
  1014.         before = firstwin;
  1015.     else
  1016.         before = after->w_next;
  1017.  
  1018.     wp->w_next = before;
  1019.     wp->w_prev = after;
  1020.     if (after == NULL)
  1021.         firstwin = wp;
  1022.     else
  1023.         after->w_next = wp;
  1024.     if (before == NULL)
  1025.         lastwin = wp;
  1026.     else
  1027.         before->w_prev = wp;
  1028. }
  1029.  
  1030. /*
  1031.  * remove window from the window list
  1032.  */
  1033.     static void
  1034. win_remove(wp)
  1035.     WIN        *wp;
  1036. {
  1037.     if (wp->w_prev)
  1038.         wp->w_prev->w_next = wp->w_next;
  1039.     else
  1040.         firstwin = wp->w_next;
  1041.     if (wp->w_next)
  1042.         wp->w_next->w_prev = wp->w_prev;
  1043.     else
  1044.         lastwin = wp->w_prev;
  1045. }
  1046.  
  1047. /*
  1048.  * allocate lsize arrays for a window
  1049.  * return FAIL for failure, OK for success
  1050.  */
  1051.     int
  1052. win_alloc_lsize(wp)
  1053.     WIN        *wp;
  1054. {
  1055.     wp->w_lsize_valid = 0;
  1056.     wp->w_lsize_lnum = (linenr_t *) malloc((size_t) (Rows * sizeof(linenr_t)));
  1057.     wp->w_lsize = (char_u *)malloc((size_t) Rows);
  1058.     if (wp->w_lsize_lnum == NULL || wp->w_lsize == NULL)
  1059.     {
  1060.         win_free_lsize(wp);        /* one of the two may have worked */
  1061.         wp->w_lsize_lnum = NULL;
  1062.         wp->w_lsize = NULL;
  1063.         return FAIL;
  1064.     }
  1065.     return OK;
  1066. }
  1067.  
  1068. /*
  1069.  * free lsize arrays for a window
  1070.  */
  1071.      void
  1072. win_free_lsize(wp)
  1073.     WIN        *wp;
  1074. {
  1075.     vim_free(wp->w_lsize_lnum);
  1076.     vim_free(wp->w_lsize);
  1077. }
  1078.  
  1079. /*
  1080.  * call this fuction whenever Rows changes value
  1081.  */
  1082.     void
  1083. screen_new_rows()
  1084. {
  1085.     WIN        *wp;
  1086.     int        extra_lines;
  1087.  
  1088.     if (firstwin == NULL)        /* not initialized yet */
  1089.         return;
  1090. /*
  1091.  * the number of extra lines is the difference between the position where
  1092.  * the command line should be and where it is now
  1093.  */
  1094.     compute_cmdrow();
  1095.     extra_lines = Rows - p_ch - cmdline_row;
  1096.     if (extra_lines < 0)                        /* reduce windows height */
  1097.     {
  1098.         for (wp = lastwin; wp; wp = wp->w_prev)
  1099.         {
  1100.             if (wp->w_height - MIN_ROWS < -extra_lines)
  1101.             {
  1102.                 extra_lines += wp->w_height - MIN_ROWS;
  1103.                 win_new_height(wp, MIN_ROWS);
  1104.             }
  1105.             else
  1106.             {
  1107.                 win_new_height(wp, wp->w_height + extra_lines);
  1108.                 break;
  1109.             }
  1110.         }
  1111.         (void)win_comp_pos();                /* compute w_winpos */
  1112.     }
  1113.     else if (extra_lines > 0)                /* increase height of last window */
  1114.         win_new_height(lastwin, lastwin->w_height + extra_lines);
  1115.  
  1116.     compute_cmdrow();
  1117.  
  1118.     if (p_ea)
  1119.         win_equal(curwin, FALSE);
  1120. }
  1121.  
  1122. /*
  1123.  * update the w_winpos field for all windows
  1124.  * returns the row just after the last window
  1125.  */
  1126.     static int
  1127. win_comp_pos()
  1128. {
  1129.     WIN        *wp;
  1130.     int        row;
  1131.  
  1132.     row = 0;
  1133.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1134.     {
  1135.         if (wp->w_winpos != row)        /* if position changes, redraw */
  1136.         {
  1137.             wp->w_winpos = row;
  1138.             wp->w_redr_type = NOT_VALID;
  1139.             wp->w_redr_status = TRUE;
  1140.         }
  1141.         row += wp->w_height + wp->w_status_height;
  1142.     }
  1143.     return row;
  1144. }
  1145.  
  1146. /*
  1147.  * set current window height
  1148.  */
  1149.     void
  1150. win_setheight(height)
  1151.     int        height;
  1152. {
  1153.     WIN        *wp;
  1154.     int        room;                /* total number of lines available */
  1155.     int        take;                /* number of lines taken from other windows */
  1156.     int        room_cmdline;        /* lines available from cmdline */
  1157.     int        row;
  1158.     int        run;
  1159.  
  1160.     if (height < MIN_ROWS)        /* need at least some lines */
  1161.         height = MIN_ROWS;
  1162. /*
  1163.  * compute the room we have from all the windows
  1164.  */
  1165.     room = MIN_ROWS;            /* count the MIN_ROWS for the current window */
  1166.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1167.         room += wp->w_height - MIN_ROWS;
  1168. /*
  1169.  * compute the room available from the command line
  1170.  */
  1171.     room_cmdline = Rows - p_ch - cmdline_row;
  1172. /*
  1173.  * limit new height to the room available
  1174.  */
  1175.     if (height > room + room_cmdline)        /* can't make it that large */
  1176.         height = room + room_cmdline;        /* use all available room */
  1177. /*
  1178.  * compute the number of lines we will take from the windows (can be negative)
  1179.  */
  1180.     take = height - curwin->w_height;
  1181.     if (take == 0)                            /* no change, nothing to do */
  1182.         return;
  1183.  
  1184.     if (take > 0)
  1185.     {
  1186.         take -= room_cmdline;                /* use lines from cmdline first */
  1187.         if (take < 0)
  1188.             take = 0;
  1189.     }
  1190. /*
  1191.  * set the current window to the new height
  1192.  */
  1193.     win_new_height(curwin, height);
  1194.  
  1195. /*
  1196.  * First take lines from the windows below the current window.
  1197.  * If that is not enough, takes lines from windows above the current window.
  1198.  */
  1199.     for (run = 0; run < 2; ++run)
  1200.     {
  1201.         if (run == 0)
  1202.             wp = curwin->w_next;        /* 1st run: start with next window */
  1203.         else
  1204.             wp = curwin->w_prev;        /* 2nd run: start with prev window */
  1205.         while (wp != NULL && take != 0)
  1206.         {
  1207.             if (wp->w_height - take < MIN_ROWS)
  1208.             {
  1209.                 take -= wp->w_height - MIN_ROWS;
  1210.                 win_new_height(wp, MIN_ROWS);
  1211.             }
  1212.             else
  1213.             {
  1214.                 win_new_height(wp, wp->w_height - take);
  1215.                 take = 0;
  1216.             }
  1217.             if (run == 0)
  1218.                 wp = wp->w_next;
  1219.             else
  1220.                 wp = wp->w_prev;
  1221.         }
  1222.     }
  1223.  
  1224. /* recompute the window positions */
  1225.     row = win_comp_pos();
  1226.  
  1227. /*
  1228.  * If there is extra space created between the last window and the command line,
  1229.  * clear it.
  1230.  */
  1231.     screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ');
  1232.     cmdline_row = row;
  1233.  
  1234.     updateScreen(NOT_VALID);
  1235. }
  1236.  
  1237. #ifdef USE_MOUSE
  1238.     void
  1239. win_drag_status_line(offset)
  1240.     int        offset;
  1241. {
  1242.     WIN        *wp;
  1243.     int        room;
  1244.     int        row;
  1245.     int        up;                /* if TRUE, drag status line up, otherwise down */
  1246.  
  1247.     if (offset < 0)
  1248.     {
  1249.         up = TRUE;
  1250.         offset = -offset;
  1251.     }
  1252.     else
  1253.         up = FALSE;
  1254.  
  1255.     if (up)    /* drag up */
  1256.     {
  1257.         room = 0;
  1258.         for (wp = curwin; wp != NULL && room < offset; wp = wp->w_prev)
  1259.             room += wp->w_height - MIN_ROWS;
  1260.         wp = curwin->w_next;                /* put wp at window that grows */
  1261.     }
  1262.     else    /* drag down */
  1263.     {
  1264.         /*
  1265.          * Only dragging the last status line can reduce p_ch.
  1266.          */
  1267.         room = Rows - cmdline_row;
  1268.         if (curwin->w_next == NULL)
  1269.             room -= 1;
  1270.         else
  1271.             room -= p_ch;
  1272.         for (wp = curwin->w_next; wp != NULL && room < offset; wp = wp->w_next)
  1273.             room += wp->w_height - MIN_ROWS;
  1274.         wp = curwin;                        /* put wp at window that grows */
  1275.     }
  1276.  
  1277.     if (room < offset)        /* Not enough room */
  1278.         offset = room;        /* Move as far as we can */
  1279.     if (offset <= 0)
  1280.         return;
  1281.  
  1282.     if (wp != NULL)            /* grow window wp by offset lines */
  1283.         win_new_height(wp, wp->w_height + offset);
  1284.  
  1285.     if (up)
  1286.         wp = curwin;                /* current window gets smaller */
  1287.     else
  1288.         wp = curwin->w_next;        /* next window gets smaller */
  1289.  
  1290.     while (wp != NULL && offset > 0)
  1291.     {
  1292.         if (wp->w_height - offset < MIN_ROWS)
  1293.         {
  1294.             offset -= wp->w_height - MIN_ROWS;
  1295.             win_new_height(wp, MIN_ROWS);
  1296.         }
  1297.         else
  1298.         {
  1299.             win_new_height(wp, wp->w_height - offset);
  1300.             offset = 0;
  1301.         }
  1302.         if (up)
  1303.             wp = wp->w_prev;
  1304.         else
  1305.             wp = wp->w_next;
  1306.     }
  1307.     row = win_comp_pos();
  1308.     screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ');
  1309.     cmdline_row = row;
  1310.     p_ch = Rows - cmdline_row;
  1311.     updateScreen(NOT_VALID);
  1312.     showmode();
  1313. }
  1314. #endif /* USE_MOUSE */
  1315.  
  1316. /*
  1317.  * Set new window height.
  1318.  */
  1319.     static void
  1320. win_new_height(wp, height)
  1321.     WIN        *wp;
  1322.     int        height;
  1323. {
  1324.     /* should adjust topline to keep cursor at same relative postition */
  1325.  
  1326.     wp->w_height = height;
  1327.     win_comp_scroll(wp);
  1328.     wp->w_redr_type = NOT_VALID;
  1329.     wp->w_redr_status = TRUE;
  1330. }
  1331.  
  1332.     void
  1333. win_comp_scroll(wp)
  1334.     WIN        *wp;
  1335. {
  1336.     wp->w_p_scroll = (wp->w_height >> 1);
  1337.     if (wp->w_p_scroll == 0)
  1338.         wp->w_p_scroll = 1;
  1339. }
  1340.  
  1341. /*
  1342.  * command_height: called whenever p_ch has been changed
  1343.  */
  1344.     void
  1345. command_height()
  1346. {
  1347.     int        current;
  1348.  
  1349.     current = Rows - cmdline_row;
  1350.     if (p_ch > current)                /* p_ch got bigger */
  1351.     {
  1352.         if (lastwin->w_height - (p_ch - current) < MIN_ROWS)
  1353.         {
  1354.             emsg(e_noroom);
  1355.             p_ch = lastwin->w_height - MIN_ROWS + current;
  1356.         }
  1357.         /* clear the lines added to cmdline */
  1358.         screen_fill((int)(Rows - p_ch), (int)Rows, 0, (int)Columns, ' ', ' ');
  1359.     }
  1360.     win_new_height(lastwin, lastwin->w_height + current - (int)p_ch);
  1361.     cmdline_row = Rows - p_ch;
  1362.     redraw_cmdline = TRUE;
  1363. }
  1364.  
  1365.     void
  1366. last_status()
  1367. {
  1368.     if (lastwin->w_status_height)
  1369.     {
  1370.                     /* remove status line */
  1371.         if (p_ls == 0 || (p_ls == 1 && firstwin == lastwin))
  1372.         {
  1373.             win_new_height(lastwin, lastwin->w_height + 1);
  1374.             lastwin->w_status_height = 0;
  1375.         }
  1376.     }
  1377.     else
  1378.     {
  1379.                     /* add status line */
  1380.         if (p_ls == 2 || (p_ls == 1 && firstwin != lastwin))
  1381.         {
  1382.             if (lastwin->w_height <= MIN_ROWS)        /* can't do it */
  1383.                 emsg(e_noroom);
  1384.             else
  1385.             {
  1386.                 win_new_height(lastwin, lastwin->w_height - 1);
  1387.                 lastwin->w_status_height = 1;
  1388.             }
  1389.         }
  1390.     }
  1391. }
  1392.  
  1393. /*
  1394.  * file_name_at_cursor()
  1395.  *
  1396.  * Return the name of the file under (or to the right of) the cursor.  The
  1397.  * p_path variable is searched if the file name does not start with '/'.
  1398.  * The string returned has been alloc'ed and should be freed by the caller.
  1399.  * NULL is returned if the file name or file is not found.
  1400.  */
  1401.     char_u *
  1402. file_name_at_cursor(options)
  1403.     int        options;
  1404. {
  1405.     return get_file_name_in_path(ml_get_curline(),
  1406.                                                curwin->w_cursor.col, options);
  1407. }
  1408. /* options:
  1409.  * FNAME_MESS        give error messages
  1410.  * FNAME_EXP        expand to path
  1411.  * FNAME_HYP        check for hypertext link
  1412.  */
  1413.     char_u *
  1414. get_file_name_in_path(ptr, col, options)
  1415.     char_u    *ptr;
  1416.     int        col;
  1417.     int        options;
  1418. {
  1419.     char_u    *dir;
  1420.     char_u    *file_name;
  1421.     char_u    save_char;
  1422.     char_u    *curr_path = NULL;
  1423.     int        curr_path_len;
  1424.     int        len;
  1425.  
  1426.         /* search forward for what could be the start of a file name */
  1427.     while (ptr[col] != NUL && !isfilechar(ptr[col]))
  1428.         ++col;
  1429.     if (ptr[col] == NUL)            /* nothing found */
  1430.     {
  1431.         if (options & FNAME_MESS)
  1432.             EMSG("No file name under cursor");
  1433.         return NULL;
  1434.     }
  1435.  
  1436.         /* search backward for char that cannot be in a file name */
  1437.     while (col >= 0 && isfilechar(ptr[col]))
  1438.         --col;
  1439.     ptr += col + 1;
  1440.     col = 0;
  1441.  
  1442.         /* search forward for a char that cannot be in a file name */
  1443.     while (isfilechar(ptr[col]))
  1444.         ++col;
  1445.  
  1446.     if (options & FNAME_HYP)
  1447.     {
  1448.         /* For hypertext links, ignore the name of the machine.
  1449.          * Such a link looks like "type://machine/path". Only "/path" is used.
  1450.          * First search for the string "://", then for the extra '/'
  1451.          */
  1452.         if ((file_name = vim_strchr(ptr, ':')) != NULL &&
  1453.                 STRNCMP(file_name, "://", (size_t)3) == 0 &&
  1454.                 (file_name = vim_strchr(file_name + 3, '/')) != NULL &&
  1455.                 file_name < ptr + col)
  1456.         {
  1457.             col -= file_name - ptr;
  1458.             ptr = file_name;
  1459.             if (ptr[1] == '~')        /* skip '/' for /~user/path */
  1460.             {
  1461.                 ++ptr;
  1462.                 --col;
  1463.             }
  1464.         }
  1465.     }
  1466.  
  1467.     if (!(options & FNAME_EXP))
  1468.         return strnsave(ptr, col);
  1469.  
  1470.         /* copy file name into NameBuff, expanding environment variables */
  1471.     save_char = ptr[col];
  1472.     ptr[col] = NUL;
  1473.     expand_env(ptr, NameBuff, MAXPATHL);
  1474.     ptr[col] = save_char;
  1475.  
  1476.     if (isFullName(NameBuff))            /* absolute path */
  1477.     {
  1478.         if ((file_name = strsave(NameBuff)) == NULL)
  1479.             return NULL;
  1480.         if (getperm(file_name) >= 0)
  1481.             return file_name;
  1482.         if (options & FNAME_MESS)
  1483.         {
  1484.             sprintf((char *)IObuff, "Can't find file `%s'", NameBuff);
  1485.             emsg(IObuff);
  1486.         }
  1487.     }
  1488.     else                            /* relative path, use 'path' option */
  1489.     {
  1490.         if (curbuf->b_xfilename != NULL)
  1491.         {
  1492.             curr_path = curbuf->b_xfilename;
  1493.             ptr = gettail(curr_path);
  1494.             curr_path_len = ptr - curr_path;
  1495.         }
  1496.         else
  1497.             curr_path_len = 0;
  1498.         if ((file_name = alloc((int)(curr_path_len + STRLEN(p_path) +
  1499.                                             STRLEN(NameBuff) + 3))) == NULL)
  1500.             return NULL;
  1501.  
  1502.         for (dir = p_path; *dir;)
  1503.         {
  1504.             len = copy_option_part(&dir, file_name, 31000, " ,");
  1505.             /* len == 0 means: use current directory */
  1506.             if (len != 0)
  1507.             {
  1508.                                 /* Look for file relative to current file */
  1509.                 if (file_name[0] == '.' && curr_path_len > 0)
  1510.                 {
  1511.                     if (len == 1)        /* just a "." */
  1512.                         len = 0;
  1513.                     else                /* "./path": move "path" */
  1514.                     {
  1515.                         len -= 2;
  1516.                         vim_memmove(file_name + curr_path_len, file_name + 2,
  1517.                                                                  (size_t)len);
  1518.                     }
  1519.                     STRNCPY(file_name, curr_path, curr_path_len);
  1520.                     len += curr_path_len;
  1521.                 }
  1522.                 if (!ispathsep(file_name[len - 1]))
  1523.                     file_name[len++] = PATHSEP;
  1524.             }
  1525.             STRCPY(file_name + len, NameBuff);
  1526.             if (getperm(file_name) >= 0)
  1527.                 return file_name;
  1528.         }
  1529.         if (options & FNAME_MESS)
  1530.             EMSG2("Can't find file \"%s\" in path", NameBuff);
  1531.     }
  1532.     vim_free(file_name);            /* file doesn't exist */
  1533.     return NULL;
  1534. }
  1535.  
  1536. /*
  1537.  * Return the minimal number of rows that is needed on the screen to display
  1538.  * the current number of windows.
  1539.  */
  1540.     int
  1541. min_rows()
  1542. {
  1543.     WIN        *wp;
  1544.     int        total;
  1545.  
  1546.     if (firstwin == NULL)        /* not initialized yet */
  1547.         return MIN_ROWS + 1;    /* one window plus command line */
  1548.  
  1549.     total = p_ch;        /* count the room for the status line */
  1550.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1551.         total += MIN_ROWS + wp->w_status_height;
  1552.     return total;
  1553. }
  1554.  
  1555. /*
  1556.  * Return TRUE if there is only one window, not counting a help window, unless
  1557.  * it is the current window.
  1558.  */
  1559.     int
  1560. only_one_window()
  1561. {
  1562.     int        count = 0;
  1563.     WIN        *wp;
  1564.  
  1565.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1566.         if (!wp->w_buffer->b_help || wp == curwin)
  1567.             ++count;
  1568.     return (count <= 1);
  1569. }
  1570.